<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>立方体绘制</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      canvas {
        margin: 50px auto 0;
        display: block;
        background: yellow;
      }
    </style>
    <script src="test.js"></script>
  </head>
  <body>
    <canvas id="canvas" width="400" height="400"></canvas>
    <script>
      // 获取
      const canvas = document.getElementById('canvas')
      const gl = canvas.getContext('webgl')
      // 定义片源着色器和顶点着色器
      const vsSource = `
        attribute vec4 apos;
        attribute vec4 acolor;
        varying vec4 vcolor;
        uniform mat4 u_projectionMatrix;
      uniform mat4 u_viewMatrix;
        void main() {
          gl_Position =u_projectionMatrix * u_viewMatrix * apos;
          vcolor = acolor;
        }
      `
      const fsSource = `
        precision mediump float;
        varying vec4 vcolor;
        void main() {
          gl_FragColor = vcolor;
        }
      `
      //   初始化webgl
      const program = initShader(gl, vsSource, fsSource)
      gl.useProgram(program)
      // 获取 uniform 位置
      const u_projectionMatrixLocation = gl.getUniformLocation(program, 'u_projectionMatrix')
      const u_viewMatrixLocation = gl.getUniformLocation(program, 'u_viewMatrix')
      // 创建透视矩阵和视图矩阵
      const fov = Math.PI / 9 // 45度视角
      const aspect = canvas.width / canvas.height
      const near = 0.2
      const far = 50.0

      const projectionMatrix = perspectiveMatrix(fov, aspect, near, far)
      const viewMatrix = lookAtMatrix([3, 3, 7], [0, 0, 0], [0, 1, 0])
      function lookAtMatrix(eye, center, up) {
        const zAxis = normalize(subtractVectors(eye, center))
        const xAxis = normalize(cross(up, zAxis))
        const yAxis = normalize(cross(zAxis, xAxis))

        return new Float32Array([
          xAxis[0],
          yAxis[0],
          zAxis[0],
          0,
          xAxis[1],
          yAxis[1],
          zAxis[1],
          0,
          xAxis[2],
          yAxis[2],
          zAxis[2],
          0,
          -dot(xAxis, eye),
          -dot(yAxis, eye),
          -dot(zAxis, eye),
          1
        ])
      }

      function normalize(v) {
        const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
        return [v[0] / length, v[1] / length, v[2] / length]
      }

      function subtractVectors(a, b) {
        return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
      }

      function cross(a, b) {
        return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]
      }

      function dot(a, b) {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
      }

      function perspectiveMatrix(fov, aspect, near, far) {
        const f = 1.0 / Math.tan(fov / 2)
        const nf = 1 / (near - far)

        return new Float32Array([f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (far + near) * nf, -1, 0, 0, 2 * far * near * nf, 0])
      }

      // 设置投影矩阵和视图矩阵
      gl.uniformMatrix4fv(u_projectionMatrixLocation, false, projectionMatrix)
      gl.uniformMatrix4fv(u_viewMatrixLocation, false, viewMatrix)
      // 创建缓存区
      function initBuffer(gl, program) {
        const aposLocation = gl.getAttribLocation(program, 'apos')
        const acolorLocation = gl.getAttribLocation(program, 'acolor')
        const verticesColors = new Float32Array([
          -0.5, -0.5, 0.5, 1, 0, 0, 1, 0.5, -0.5, 0.5, 1, 0, 0, 1, 0.5, 0.5, 0.5, 1, 0, 0, 1, -0.5, 0.5, 0.5, 1, 0, 0, 1,

          -0.5, 0.5, 0.5, 0, 1, 0, 1, -0.5, 0.5, -0.5, 0, 1, 0, 1, -0.5, -0.5, -0.5, 0, 1, 0, 1, -0.5, -0.5, 0.5, 0, 1, 0, 1,

          0.5, 0.5, 0.5, 0, 0, 1, 1, 0.5, -0.5, 0.5, 0, 0, 1, 1, 0.5, -0.5, -0.5, 0, 0, 1, 1, 0.5, 0.5, -0.5, 0, 0, 1, 1,

          0.5, 0.5, -0.5, 1, 0, 1, 1, 0.5, -0.5, -0.5, 1, 0, 1, 1, -0.5, -0.5, -0.5, 1, 0, 1, 1, -0.5, 0.5, -0.5, 1, 0, 1, 1,

          -0.5, 0.5, 0.5, 1, 1, 0, 1, 0.5, 0.5, 0.5, 1, 1, 0, 1, 0.5, 0.5, -0.5, 1, 1, 0, 1, -0.5, 0.5, -0.5, 1, 1, 0, 1,

          -0.5, -0.5, 0.5, 0, 1, 1, 1, -0.5, -0.5, -0.5, 0, 1, 1, 1, 0.5, -0.5, -0.5, 0, 1, 1, 1, 0.5, -0.5, 0.5, 0, 1, 1, 1
        ])

        const indices = new Uint16Array([
          0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23
        ])

        const vertexColorBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW)

        const indexBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

        const FSIZE = verticesColors.BYTES_PER_ELEMENT
        // 启用顶点属性并指向顶点缓冲区中的数据
        gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, FSIZE * 7, 0)
        gl.enableVertexAttribArray(aposLocation)

        gl.vertexAttribPointer(acolorLocation, 4, gl.FLOAT, false, FSIZE * 7, FSIZE * 3)
        gl.enableVertexAttribArray(acolorLocation)
      }
      initBuffer(gl, program)
      gl.clearColor(0, 0, 0, 1)
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
      gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0)
    </script>
  </body>
</html>
